package org.smth.search.indexer.impl;

import org.apache.lucene.analysis.Token;
import org.apache.lucene.analysis.TokenStream;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.Queue;
import java.util.LinkedList;

public class EnumAllTokenStream extends TokenStream {

	public static final int[] DefaultSeps = {
			' ', //英文空格
			'　', //中文空格
			'\r', '\n', '\t', '，', '。', '：', '；', '、', '】', '【', '《', '》', '＜', '＞', '”', '“', '｛', '｝', '〔', '〕', '（',
			'）', '［', '］', '？', '！', '…', '·', '＇', '＂', '∶', '‖', '～', '’', '‘', '〈', '〉', '「', '」', '『', '』', '—',
			'－', //中文标点
			'-', //减号
			':', ',', '(', ')', '[', ']', '{', '}', ';', '!', '<', '>', '|' //英文标点
			, '.', '*', '_', '/', '\\', '@', '"', '\'' //英文标点
	};

	static {
		Arrays.sort(DefaultSeps);
	}

	private Reader reader;

	private int minch;

	private int maxch;

	private int minen;

	private int maxen;

	public int getMinch() {
		return minch;
	}

	public void setMinch(int minch) {
		this.minch = minch;
	}

	public int getMaxch() {
		return maxch;
	}

	public void setMaxch(int maxch) {
		this.maxch = maxch;
	}

	public int getMinen() {
		return minen;
	}

	public void setMinen(int minen) {
		this.minen = minen;
	}

	public int getMaxen() {
		return maxen;
	}

	public void setMaxen(int maxen) {
		this.maxen = maxen;
	}

	public EnumAllTokenStream(Reader reader, int minch, int maxch, int minen, int maxen) {
		this.reader = reader;
		this.minch = minch;
		this.maxch = maxch;
		this.minen = minen;
		this.maxen = maxen;
	}

	private static enum CharType {
		Eof, Sep, Letter, Chinese
	}

	private CharType type;

	private int push = -1;

	private int pos = 0;

	protected void push(int c) {
		push = c;
		pos--;
	}

	protected boolean isWideChar(int c) {
		return c >= 128;
	}

	protected int read() throws IOException {
		int c;
		if (push >= 0) {
			c = push;
			push = -1;
		} else {
			if (type == CharType.Eof) {
				return -1;
			}
			c = reader.read();
		}
		pos++;
		if (c == -1) {
			type = CharType.Eof;
		} else if (Arrays.binarySearch(DefaultSeps, c) >= 0) {
			type = CharType.Sep;
		} else if (isWideChar(c)) {
			type = CharType.Chinese;
		} else {
			type = CharType.Letter;
		}
		return c;
	}

	private int offset = 0;

	private char[] chars;

	protected void readNextBuffer() throws IOException {
		int c;
		offset = pos - 1;
		do {
			offset++;
			c = read();
		} while (type == CharType.Sep);
		if (type != CharType.Eof) {
			CharArrayWriter writer = new CharArrayWriter();
			CharType mytype = type;
			while (type == mytype) {
				writer.write(c);
				c = read();
			}
			if (type != CharType.Eof && type != CharType.Sep) {
				push(c);
			}
			chars = writer.toCharArray();
		} else {
			chars = null;
		}
	}

	private Queue<Token> queue = new LinkedList<Token>();

	private int loc = 0;

	protected void readTokens() throws IOException {
		if (chars == null) {
			readNextBuffer();
			loc = 0;
		}
		if (chars != null) {
			int len = chars.length;
			int min, max;
			if (isWideChar(chars[0])) {
				min = minch;
				max = maxch;
			} else {
				min = minen;
				max = maxen;
			}
			if (len < min) {
				Token token = new Token(new String(chars, 0, len).toLowerCase(), offset, offset + len);
				queue.offer(token);
				chars = null;
			} else {
				for (int i = min; i <= max; i++) {
					if ((loc + i) > len) {
						break;
					}
					Token token = new Token(new String(chars, loc, i).toLowerCase(), offset + loc, offset + loc + i);
					queue.offer(token);
				}
				loc++;
				if (len - loc < min) {
					chars = null;
				}
			}
		}
	}

	public Token next() throws IOException {
		if (queue.isEmpty()) {
			readTokens();
		}
		return queue.poll();
	}

}
